Common Lisp Notes
Table of Contents
Resources
Source Files
- Common Lisp source files can have the .lispextension
Multi-Paradigm
- ANSI Common Lisp is multi paradigm
- It supports functional, generic, object oriented and domain specific programming styles.
SLIME
- Common Lisp development environment for Emacs
- The official webpage is, https://common-lisp.net/project/slime/
- To use with org babel ensure that lispis enabled in yourorg-babel-load-langauges
- Start slime M-x slimebefore attempting to evaluate a common lisp source block withC-c C-c
- You should be able to access variables and functions inside the SLIME REPL that are defined in the source code blocks
- Use C-c C-d dto view the documentation for the symbol at point- This might show you a link to http://www.ai.mit.edu to get the real documentation
 
CLISP
- Easy to use and portable common lisp compiler that runs on most operating systems
- Also provides debugger and interpreter
SBCL
- Steel Bank Common Lisp descends from Carnegie Mellon, Steel being Andrew Carnegie's industry and Banking being Andrew Mellon's industry
- Considered more heavy duty than CLISP
- Also provides debugger and interpreter
Common Lisp Syntax
Anonymous (Lambda) Functions
- This creates a lambda function that doubles the input (lambda (x) (* 2 x))
Cons Pairs (Dotted Lists) vs Lists
- A list is really a chain of cons that ends with an empty list or nil
- For exmaple '(1 2 3)isequalto(cons 1 (cons 2 (cons 3 nil))
- Any cons cell that doesn't end in nil is known as a dotted list
- (cons 1 2)is displayed by the REPL as- (1 . 2)
- You can create dotted lists with a quote as well, '(1 . 2)
- You can make a list using quote and dot, '(1 . (2 . (3 . nil)))is equal to'(1 2 3)
- (cdr '(1 . 2))is- 2not- (2)
- (cdr '(1 2))is- (2)not- 2
- Cons pairs are useful for x/y coordinates and key value pairs
Circular Lists
- When using circular lists you should set the global *print-circle*to true,(setf **print-circle** t)
- The above enables complex printing features to be enabled when using self-referential lists
- Below is an example of creating a circular list
(defparameter x '(1 2 3)) (setf (cdddr x) x)
- You can use infinitely large index values in the above example
- For instance (nth 1000 x)would return2
Literals
Char
- You can represent a character literal with a #followed by a backward slash and the character
- For example #\ais the representation of the charactera
Newline
- #\newline
Tab
- #\tab
Space
- #\space
nil
- nil can be represented with nil,'nil,(), or'()
- nilis the only Lisp object that is both a symbol and a list
Hex Numbers
- #xFF
Symbols
- Symbols in Common Lisp are case insensitive
- It is common to only use lowercase when writing common lisp source code
- To make a symbol that is case sensitive surround it with pipe, |, characters- This also allows you to use punctuation in a symbol name
- |Ca$e mATT3rz|is an example
 
Quoting
- Use the single quote 'to quote a list
- To quasiquote use the backtick `and the comma to unquote,
- `(one plus two is ,(+ 1 2))
- The #before the quote is used to indicate the quoted symbol is a function
- Common Lisp is a LISP-2 rather than a LISP-1 like Scheme
- This means it has 2 separate scope for functions and data
- You can have a function and data with the same symbol name
- The caveat is you must use the #when quoting a function
 
- When quasiquoting use a splicing command ,@lists are spread when the quoted code is expanded
Variables
Globals
- Use defparameterto create global variables- The defparameterfunction will mutate the value of a global if it already exists
 
- The 
- It is a common practice to surround global variables with earmuffs (asterisks)
- For example a global named num would look like this *num*
- Another option to create globals is defvar- If the variable already exists defvarwill not change the existing value
 
- If the variable already exists 
Local variables
- Use letfunction to define local variables, you cannot reference other variables in the list
- The let*function is the same asletbut it lets you reference preceding variable names
(let* ((x 10)
       (y (* 2 x)))
  (+ x y))
Mutating
- Use setfto change the value of an existing variable
Functions
Globals
- Use defunto define a global function
(defun function_name (arguments) ...body)
Local Named Functions
- To define a local function use the fletform
- fletis very similar to- letfor variables but instead
(flet ((function_name (arguments)
         ...function body))
  ...body)
- If you want to reference other local functions in an fletyou should uselabelsinstead
- You can also labelsto call a local named function recursively
(labels ((function_a (n)
           (+ n 2))
         (function_b (n)
           (function_a (function_a n))))
  (function_b 2))
Argument Limits
- There is a limit on the number of arguments a function can have
- You can check those limits in the Common Lisp REPL with call-arguments-limit
Optional Arguments
- Adding &optionalbefore an argument marks it as optional
- This means that when the function is evaluated if you do not supply that argument it will not error
(defun fn-opt (not-optional &optional (val 10)) (list not-optional val)) (fn-opt 100)
- The above example shows an optional argument valwith a default value of10
- It returns (100 10)
Variadic Arguments
- Using &restfollowed by a variable name collects a variable amount of arguments into a list
(defun fn-var (x &rest args) (append (if (listp x) x (list x)) args)) (fn-var (fn-var 10) 100 10 10)
- The above example returns the list (10 100 10 10)
Keyword Arguments
- Using &keyall of the arguments appearing after it will be named
- To use a named argument it must be formatted with :<NAME_OF_ARG
- The preceding colon is a keyword form
(defun fn-key (&key x y w h) (list x y w h)) (fn-key :w 20 :h 10 :x 5 :y 2)
- The above example returns the list (5 2 20 10)
- It shows how when the arguments are used as keywords the order does not matter
Body Argument
- The &bodyis the same as&restbut it allows editors to indent differently for the remainder since it is abody
- &bodycan only be used with macros
Default Values
- Optional and keyword arguments can have a default value
- To specify the default value you create a list with that starts with the local name of the argument followed by the default value
- Additionally you can supply a third item to the list that is a boolean indicating if the value was set or not
(defun fn-default (&optional (val 12 val-set)) (list val val-set)) (fn-default 100)
- The above example returns the list (100 t)
Closures
- Referencing variables in a lambda expression can prevent values from being garbage collected
- This can be useful for memoizing functions
(let ((count 0)) (defun say-hello-count () (incf count) (format nil "Hello, for the ~a time" count))) (say-hello-count) (say-hello-count)
- The above example shows using lexical binding to capture a closure variable inside the function
- The count variable is incremented each time you call say-hello-count
- The defunmacro will allow you to callsay-hello-countoutside of thelet
Get the function bound to a symbol
- Use the symbol-functionfunction to get the function bound to a symbol
- This is useful if you want to create a higher order function that wraps an existing function and you need to store the original function
Tail Call Optimization
- Tail call optimization is a way to get additional performance with recursive functions
- Normally when a function has a recursive call it will add to the stack
- If the function has too many recursive calls it can lead to a stack overflow
- If the recursive call is the final statement in the function the lisp compiler can recognize this as a candidate for tail call optimization
- The tail call optimization is the compiler not adding to the stack since you are already at the correct location
- Not all lisp compilers support this since it is not part of the standard, unlike Scheme which requires tail call optimization
- In clisp you need to explicitly compilethe function to get the tail call optimization
Nullary Functions (Thunks)
- A nullary function is a function that has no arguments
- They are also commonly known as thunks or suspensions
Returning multiple values
- It is possible to write a function that returns multiple values
- When returning multiple values the first value returned is given preference when chaining the function
- This behavior could be achieved with a list but if it is a special case when you need more than one value this could be cleaner
values
- Use the valuesfunction to return multiple values from a function
(defun multi-v () (values 'foo 'bar)) (multi-v)
- The above example will return 'foo; 'bar
multiple-value-bind
- To access all of the values returned by a function use multiple-value-bind
- This allows you to name the values returned by the function and access them in a letstyle block
(multiple-value-bind (x y) ((lambda () (values 'foo 'bar))) (list x y))
- The above example will return the list (foo bar)
Eval
- You can evaluate a quoted symbol with eval
- (eval '(+ 2 2))should return- 4
- Like evalin JavaScript it can be a huge security risk in your program
Loops
loop
- Loops can be a good alternative to recursion, especially when using an implementation that does not have tail recursion support
- The most basic form of the loopis shown below
(loop (sexp) (sexp) ... (when (predicate) (return)))
- The returncommand exits the loop
- Loop can be used to create lists
- Loop has a few keywords that tell it how to behave
- The collectkeyword specifies what you want to put into the returned list for this iteration
- The repeatkeyword indicates how many times a loop should run
(loop repeat 10
      collect 8)
- The forandfrom/tokeywords let you specify a variable local to the loop that increments each loop iteraction
- This is inclusive and on both ends
(loop for n from 1 to 10
      collect n)
- The above example will return (1 2 3 4 5 6 7 8 9 10)
- The forandbelowkeywords start from 0 and iterate through every integer less than the value afterbelow
(loop for i below 10
      collect)
- The above example will return (0 1 2 3 4 5 6 7 8 9)
- There are many special tokens that can be passed to the loopmacro that do special things
(loop for y below 10 collect (loop for x below 10 collect (cons x y)))
- The above example generates all of the 2d points between 0 - 10
- Each row is a list of cons cells of points for a given y value
(defparameter *test* '(1 2 3)) (loop for i below (length *test*) do (princ (format nil "~d = ~d~%" i (nth i *test*))))
- The above example returns the following
0 = 1 1 = 2 2 = 3
dotimes
- dotimestakes a variable and an upper bound
- It will perform the actions inside the body up to the upper bound
(dotimes (i 4) (princ (format nil "Loop #~d~%" i)))
- The above example will print the following output
Loop #0 Loop #1 Loop #2 Loop #3
Macros
- Use defmacroto define a lisp macro
- Macro definition is similar to function definition
- A macro usually return a quoted or quasi-quoted lisp expression that is expanded during compile time
- In order to view an expanded use the macroexpandfunction
- It is possible to define a macro that adds lexical variables, macros that do this are called anaphoric macros
Common Lisp Functions
Math
Incrementing
- You can use 1-to decrement by 1,(1- 10)evaluates to 9
- You can also use 1+to increment by 1,(1+ 9)evaluates to 10
- Additionally there are incfanddecfto increment or decrement a value in place
- Like setftheincfanddecffunctions must be called with a variable as a parameter
(defparameter n 0) (incf n) (incf n 2) (decf n 3)
- In the above example nstarts at 0, then goes to1, then to2and finally back to0
- The incfanddecffunctions also return the new value in addition to mutating the variable
Exponent
- Use exptto raise a number to an exponent,(expt 53 53)raises 53 to the 53rd power
Random numbers
- Use the randomfunction to generate a random number
- It takes the limit as an argument which can be either an int or a float
- The returned number will be between 0 and the limit
- If it is a float than the random number generated will also be a float
- This has the side effect of changing the internal random-state
Arithmetic Shift (bitwise shift)
- https://en.wikipedia.org/wiki/Arithmetic_shift
- Use the ashfunction which takes two arguments, the number and the amount of bits to shift left
- To shift right use a negative number
Round
- The roundfunction will take a number and return the two values, the rounded integer and the remainder
- For example, (round 16.8)returns17; -0.2
- When rounding up the remainder will be negative
Modulus (Remainder)
- Use the modfunction to get the remainder between two numbers
(mod 5 2)
- The above example returns 1
Floor / Integer Truncation
- To get the integer truncation between two numbers use the floorfunction
(floor 5 2)
- The above example returns 2
Largest and Smallest Integers
- The Common Lisp standard defines variables for the largest and smallest integers
- most-positive-fixnumis the largest integer
- most-negative-fixnumis the smallest integer
Using / to get the reciprocal
- When you use the /function with only a single value it will return the reciprocal
- For example (/ 4)returns1/4
Strings
Concatenate
- Use concatenateto join multiple strings together
- Use the symbol 'stringas the first argument to the function
- (concatenate 'string "abc" "def")should return- "abcdef"
Converting to and from character lists
- Use coercewith either thelistorstringtype
- (coerce "ABC" 'list)should return- (#\A #\B #\C)
- (coerce '(#\A #\B #\C) 'string)should return- "ABC"
- This can be used with mapcarto iterate over each character
(mapcar #'(lambda (c) (princ (format nil "char: ~C~C" c #\newline))) (coerce "abc123" 'list))
Converting symbols to strings
- Use prin1-to-stringto convert symbols to strings
- (prin1-to-string 'abc)should return "ABC"
- The write-to-stringfunction also can convert data to strings
Multi Line Strings
- Lisp will store the newline character in the string
- So the \ncharacter is not needed for multi line strings
(setq mystring "the first line the second line and the third")
- To insert a newline into a string use the formatfunction and the~Ccontrol character
- The ~Ccontrol chacter means insert a character literal, you can use the#\newlinecharacter literal to get a new line
(format nil "hello~Cworld" #\newline)
- Another way to do this is with ~%
(format nil "hello~%world")
Parsing an Integer from a String
- The parse-integerfunction can be used to parse an integer from a string
- The :radixkeyword lets you specify the base of the number
- The :junk-allowedkeyword lets you indicate you want to just return nil if there is junk in the string
(parse-integer "beef" :radix 16 :junk-allowed t)
- The above example returns the integer 48879
Getting the Integer Code of an Char
- The char-codefunction will return the integer ASCII code for the char
- The code-charwill give you the char for an integer ASCII code of a char
(code-char (char-code #\A))
- The above example will return the character A
#\A
Turning a String into a Lisp Symbol
- The internfunction can turn a string into a Lisp symbol
(intern "abc")
- The above example returns the symbol abc
- There is some overlap between readandintern, in general if theinternfunctions does what you need then it is preferable since it is safer than thereadfunction
Getting the Position of Char in String
- The positionfunction can be used to get the index of a char in a string, since a string is a sequence of characters
- See Index of item in list
Creating a Fixed Size String
- The make-stringfunction will create a string with a given length
(make-string 10 :initial-element #\x)
- The above example creates a string of length 10 filled with the char x
Format
- Wikipedia: Format (Common Lisp)
- The formatfunction takes 3 parameters, destination, control string and variadic values
- The destination nil,tor some stream
- When the destination is niltheformatfunction returns the formatted string
- When it is not nil it will return nil and send the output to either stdoutwithtor some other stream specified
Format Directives
- c- single character
- r- radix base
- d- decimal (base 10) number
- b- binary (base 2) number
- o- octal (base 8) number
- x- hexadecimal (base 16) number
- f- floating point number
- e- exponent notation for number
- g- exponent or float, pickign automatically
- $- print with monetary conventions
- a- print in human friendly manner
- s- print symbol in format compatible with read function
- w- print with printer control characters
- i- indent a logical block
- t- move cursor to column
- p- prints singular or plural suffix
- %- newline unconditionally, similar to- terpi
- &- newline conditionally, similar to- fresh-line
Examples
- Strings
 (format nil "Your message is: ~a~%" "Hello, World") 
- Left pad zeroes
 (format nil "You number is: ~3,'0d," 12)- The above example will ensure that the decimal value is 3 digits long and will pad to the left with 0if it is less than 3 digits
 
- The above example will ensure that the decimal value is 3 digits long and will pad to the left with 
- Looping with Format
 - You can loop through lists with format using ~{and~}
 (format nil "~{The list has: ~a~%~}" '("abc" "def" "ghi")) 
- You can loop through lists with format using 
Lists
push
- Adds item to the beginning of a list
- The list must be a variable
(defparameter *some-list* nil) (push 4 *some-list*) (push 3 *some-list*) (push 2 *some-list*) (push 1 *some-list*) *some-list*
last
- You can get the last element of a list with last
Using push to append
- Since a list is just a conspairs, creating a newconspair with the last element will append
- The cdrof the last element of a list is an empty list ornil
- If you pushinto that empty list you will append to the list
- For instance if ais(1 2 3)this should append 4 to the list(push 4 (cdr (last a)))
pushnew
- The pushnewfunction will only add an item to a list if it is not already in it
(defparameter *some-list* '(1 2 3)) (pushnew 1 *some-list*)
- In the above example 1 is already in the list so the results of pushnewis the same list(1 2 3)
member
- Checks to see if an item is inside a list
- (member 1 '(1 2 3 4))
- This will return true when you check if nilis in the list
find
- Use find to search through a list for the first item that matches
- The search value is the first argument
- The second argument is the list that is being searched
- The keyword parameter passed with :keytells find how to determine if the list item matches the search
- (find 20 '((a 5) (b 20) (c 6) (d 20)) :key #'cadr)should return- (b 20)
find-if
- Returns the first item in a list that satisifies the predicate
- (find-if #'oddp '(2 4 5 6))
- Returns nil if the item is not found
- This will not work when searching for nilin a list
mapcar
- Use mapcarto run a function on each element of a list
- (mapcar (lambda (n) (1+ n)) '(1 2 3))should return- (2 3 4)
- You can also run mapcarover multiple sequences
- (mapcar (lambda (m n) (list m n)) '(1 2 3) '(a b c))should return- ((1 a) (2 b) (3 c))
- mapcis a more efficient version of mapcar that does not return the list
- maplistis another variant of- mapcarthat gives the remainder of the list as an argument to the function rather than a single item
mapcan
- Similar to mapcarbut allows the lambda to return a list
- The list values are all appended together in the result
(mapcan (lambda (v) (case v (a '(1 2 3)) (b '(4 5 6 7)) (c '(8 9 10 11 12)))) '(a c b))
- The above example will return (1 2 3 8 9 10 11 12 4 5 6 7)
apply
- Use applyto call a functions once with all the elements of a list as its arguments
(defparameter *rect* '(40 40 20 30)) (defun rect-area (x y w h) (* w h)) (apply #'rect-area *rect*)
remove-if-not
- Removes all items from the list that do not satisfy the predicate
- (remove-if-not #'oddp '(1 2 3 4 5))should return- (1 3 5)
nth
- Use nthto get the value at index n from a list
- (nth 2 '(7 8 9))should return- 9
use setf and nth to change list item value
- You can use setfto mutate a list
- For example if you have a list named lwith the value(1 1 1)
- (setf (nth 2 l) 4)should mutate- lto be- (1 1 4)
subseq
- Use subseqto get a sub sequence of a list
- The start index is required and you can optionally add the end index
- The start index is inclusive and the end index is exclusive, (start end]
- (subseq '(9 8 7 6) 1 3)should return- (8 7)
Swapping list items with rotatef
- If you have the list xwith the value(1 2 3)
- You can swap the 1 and with like so (rotatef (nth 0 x) (nth 2 x))- This should return (3 2 1)
 
- This should return 
- This will mutate the list
concatenate
- Use concatenateto join multiple lists together
- Use the symbol 'listas the first argument to the function
- (concatenate 'list '(1 2 3) '(4 5 6))should return- (1 2 3 4 5 6)
Slicing an item out of a list
- You can generate a new list with a particular index sliced out using concatenateandsubseq
- If you have the list dwith the value(1 2 3 4 5)
- (concatenate 'list (subseq d 0 2) (subseq d 3))should return- (1 2 4 5)
Testing the values of a list with every some notevery notany
- These functions run a predicate and return a different boolean value based on their rules
- everyreturns nil at the first instance of a- nilvalue, similar to logical and
- somereturns true if any of the values return true
- notanyreturns nil if any of the values return true
- noteveryreturns true if all the values are false
- (every #'identity '(t t nil)should return nil
- (every #'identity '(t t t))should return true
substitute-if
- Replaces every item in a sequence with the first argument if it passes the second argument predicate
- For instance, (substitute-if 0 #'oddp '(1 2 3 4 5))will return(0 2 0 4 0)
Index of item in list
- The index of the first instance of an item in a list can be found with the positionfunction
(position 2 '(1 2 3 4 2))
- The above code will return 1since that is the index of the first 2 in the list
Difference between lists
- To get a list of the items that are difference between a list use the set-differencefunction
- This function gives you the items that are in the first list that are not in the second list
- (set-difference '(1 2 3) '(a 2 c))should return the set- (3 1), the order in the set does not necessarily match the order in the first list
- (set-difference '(a 2 c) '(1 2 3))should return the set- (c a)
Intersection between lists
- To get the intersection of two lists use the intersectionfunction
- (intersection '(1 2 3) '(a 2 c))should return- (2)
Remove Duplicates
- The remove-duplicatesfunction will return a list where no item repeats
- You can give it a custom :testfunction to use when comparing items
- (remove-duplicates '(1 1 2 2 3 3))should return- (1 2 3)
Appling a predicate to a list with some
- The somefunction will run a predicate on each item in a list in order
- The first time the predicate returns true the it stops checking the list
(some #'oddp '(2 2 4 8 6))
- The above example will return nilsince no members of the list are odd
(some #'oddp '(2 2 4 5 6))
- The above example will return tsince there is one odd member
reduce
- Iterate through a sequence and reduce it down to a single value
- The reducefunction takes two arguments, a function that reduces two items to 1 and a sequence of items
(reduce #'* '(1 2 3 4 5 6 7))
- The above example will return 5040, which is 1 * 2 * 3 * 4 * 5 * 6 * 7
- In the reducer function the first argument is the accumulated value and the second is the current item in the list
- It is possible to set an initial value for the accumulated value with the :initial-valuekey
(reduce (lambda (acc i) (if (oddp i) (+ acc (* i 2)) acc)) '(2 4 5 1) :initial-value 100)
- The above example results in 112, since the initial value is 100 and the only two odd values in the list are 5 and 1 which * 2 are 12
- The reduce function is generic and can be used on all sequence types (arrays, lists, strings)
sort
- The sort function allows you to arbitrarily sort a list
(sort '(1 2 3 4 5 6) #'>)
- The above example sorts the list in descending order
Association Lists (alists)
- Use assocto find the value of a key in an alist
(assoc 'mykey '((somekey (some-value))
                (mykey (my-value))
                (otherkey (other-value))))
- alists can have multiple instances of a key inside them
- When this happens assocwill return the first instance
- If you push new keys into the alist you can overwrite the value of a key while preserving the previous value
- You can use setfto change the value of an list,(setf (cadr (assoc '2 alist)) t)
- alists are not very efficient beyond a dozen items
- alists can also be implemented using cons pairs
(assoc 'mykey '((somekey . somevalue)
                (mykey . myvalue)
                (otherkey . othervalue)))
Logic / Conditionals
Complementing Predicates
- If you have a predicate and you want the opposite of it or complement you can use the higher order function complementto achieve that
- For example (substitute-if 0 (complement #'oddp) '(1 2 3 4 5))should return(1 0 3 0 5)
Shortcut Boolean Evaluation
- When evaluating an ororandboolean operator lisp will stop when it encounters the first symbol that evaluates to either true or false
- For example when evaluating an orthe first true that is encountered causes lisp to stop
- When evaluating an andthe first false encounted causes lisp to stop
- This allows you to build conditionals out of ororandstatements
- and
 (defun pred_a () t) (defun pred_b () t) (defun work () 'work-when) (when (pred_a) (when (pred_b) (work))) (defun pred_a () t) (defun pred_b () t) (defun work () 'work-and) (and (pred_a) (pred_b) (work)) 
- or
 (defun pred_a () nil) (defun pred_b () nil) (defun work () 'work-unless) (unless (pred_a) (unless (pred_b) (work))) (defun pred_a () nil) (defun pred_b () nil) (defun work () 'work-or) (or (pred_a) (pred_b) (work)) 
Using case to switch through multiple options
- The casefunction can be used to handle multiple conditionals for a single value
- A conditional named otherwise
(defvar x 1234) (case x (123 (princ "x is 123")) (456 (princ "x is 456")) (otherwise (princ "beats me")))
- The above example prints "beats me" since xdoes not match any of the conditionals
Equality
- Use eqfor comparing symbols- eqreturns true when two symbols point to the same- cons
- eqlwill also return true when characters and numbers are used instead of symbols
 
- Use equalfor comparing everything else- equalwill tell you if two things are isomorphic (look the same)
- equalpwill return true when strings have different capitalization, or numbers are not the same type (floats vs ints)
 
- The =comparison is meant primarily for numbers
- string-equalis specific for strings
- char-equalis specific for chars
I/O
Printing and Reading
- Use printto display a string on stdout- This will automatically add a new line at the end of the string
- prin1and- princwill not add the- newline
- printwill print values as they are stored in Lisp, so strings will have quotes and literals are displayed as such- (print #\newline)will actually print- #\newlineto stdout
 
- Use princto not add the quotation marks and use the characters the literals represent- (princ #\newline)will just print an empty line
 
- The goal of printis to output data in a way that it could be re-read back into its internal representation
 
- Use readto read from stdin- This function is called with no arguments and returns after the user has typed something and pressed enter
- You can use this to assign the value into a variable: (let ((user-input (read))))
 
- Both printandreadcan handle any Lisp data type, including symbols
- Use read-lineto read the input as a string only rather than any valid Lisp data
- Use the function fresh-lineto print a new line if the cursor is not at the beginning of a line:(fresh-line)
- The terprifunction will always print a new line even if the cursor is at the beginning of the line
- The read-sequencefunction will fill a sequence with items from a stream
Files
- with-open-fileoptionally accepts a steam and file name to open a file
- If you do not pass in an existing stream a new one is created
- With the stream variable print functions can send their output to that file
- If the stream is *standard-output*then the print functions will automatically send the output to the file
(with-open-file (stream "~/tmp/testfile.txt" :direction :output :if-exists :supersede) (princ "Hello World!" stream) (princ #\newline stream))
- The below example shows routing standard output to a file and appending to the file
- The finish-outputfunction will empty the buffers into the file
(with-open-file (*standard-output* "~/tmp/test.log" :direction :output :if-does-not-exist :create :if-exists :append) (loop repeat 100 for x from 0 do (progn (fresh-line) (format t "loop #~d" x) (finish-output) (sleep 1))))
- There is a global stream *standard-ouput*that represents stdout of the lisp environment
Arrays
- Any one dimensional array is also known as a vector
make-array
- The make-arrayfunction is used to create an array
- It takes an argument that specifies the size of the array
- It will return an array that size with each element initialized to nil
aref
- To access a member of an array use aref
- For example, say you have some-listthat has this value,#(1 2 3)to access array index 1 witharefyou would do this,(aref some-list 1), this will return 2
- arefcan be combined with- setfto mutate an array
- (setf (aref some-list 1) 5)will mutate- some-listinto- #(1 5 3)
Hash Tables
make-hash-table
- The make-hash-tablefunction will create an empty hash table
gethash
- The gethashfunction is used to access items from a hash table
- The first argument of the gethashfunction is the key, the second is the hash table
- (gethash 'foo some-hash-table)will access the- fookey in the hash table- some-hash-table
- This can also be used with setfto set values in the hash table,(setf (gethash 'foo some-hash-table) 'bar)
- The gethashfunction returns multiple values, the first is the value stored in the hash-table for the key, the second is whether or not that key was in the table
- This is needed since a key could be present in the table but have nilset as its value
Key Equality
- By default hash tables use eqto compare equality
- This will cause issues when you want to use a symbol as a key
- The equality function in a hash table can be changed using the :testkey inmake-hash-table
- (make-hash-table :test #'equal)lets you define a table whose keys are compared with the- equalfunction instead of- eq
- This is useful when you want to have keys are cons pairs
Removing entries with remhash
- The remhashfunction mirrors thegethashfunction but it removes the key from the table
(defparameter my-hash (make-hash-table)) (setf (gethash 'foo my-hash) 'bar) (remhash 'foo my-hash)
- The above example adds and removes a key from the my-hashtable
Profiling
time
- The timefunction will perform the argument function and return a lot of use profile information
Real time: 0.73878 sec. Run time: 0.738236 sec. Space: 31004976 Bytes GC: 26, GC time: 0.098185 sec.
- The above is an example of some of the profile data that the timefunction will return
Generics
setf
- In general the code for getting data out of something is the same as code for putting something in
- The setfcommand is a generic setter that can put data into data structures using the accessor as an argument
- The following setfexample changes the third item of the listfoo,(setf (third foo) 'bar)
- The first parameter in setfis a generalized reference
- A generalized reference parameter can be arbitrarily complicated, meaning whatever path needed to access the reference you will still be able to mutate it with setf
Type Predicates
- Common Lisp has dynamic typing, so a symbol can be any type
- This is a list of all the type predicates:
- arrayp
- characterp
- consp
- functionp
- hash-table-p
- listp
- stringp
- symbolp
- atom(this is the opposite of- consp, anything that is not a cons cell matches)
- nullOnly returns true if input is- nil, same as the- notpredicate
 
- These predicates an be used to implement generic functions
defmethod
- The defmethodmacro will allow you to define separate functions for each supported argument type
- This can help with code readability so you don't have have a long conditional that checks the type of the arguments
- When using defmethodyou need to explicitly state the type of each argument
(defmethod add ((x number) (y number)) (+ x y)) (defmethod add ((x string) (y string)) (format nil "string add:~%~4tx: ~a~%~4ty: ~a~%" x y)) (add (format nil "num add: ~d" (add 100 -100)) (format nil "num add: ~d" (add 2 2)))
- The above example will produce the following ouput
string add:
    x: num add: 0
    y: num add: 4
type-of
- The type-offunction will return the type of the variable
- For example, (type-of 12)will returninteger
- (type-of 'hello)will return- symbol
Exceptions / Errors
- To generate an exception or error use the errorfunction
(error "this will generate an error")
- The above example shows generating a simple error with a custom error message
Custom Error Conditions
- The function define-conditionallows you to define a custom error condition
(define-condition my-error-condition () () (:report (lambda (condition stream) (princ "custom report message for `my-error-condition'" stream)))) (error 'my-error-condition)
- The above example creates a custom error condition with a customized report
- Use the errorfunction and the symbol of the condition to trigger it
Handling Errors
- The handler-casefunction allows you to intercept an error
(define-condition my-error-condition () () (:report (lambda (condition stream) (princ "custom report message for `my-error-condition'" stream)))) (defun going-to-error () (error 'my-error-condition)) (handler-case (going-to-error) (my-error-condition () "Intercepted `my-error-condition'"))
- The above example shows using handler-caseto intercept a particular error symbol
Generate Random Symbols
- Sometimes its useful to generate a random symbol
- This can be handy for macros that need to lexically bind a value but don't want to accidentally have variables shadowed by lisp forms supplied by the user
- The gensymfunction will return a random unique symbol
Sleeping
- Common Lisp has a sleepfunction that takes the number of seconds you want to sleep as an argument
Common Lisp Data Structures
List
- Most fundamental data structure in a lisp
- A series of nested conspairs terminated with nil
- The access time for elements inside lists is not constant
Array
- Arrays are like like lists except the access time for any element is constant
- An array literal is preceded with a #to distinguish it from a list, for example,#(1 2 3)
- Arrays are in general faster than lists when accessing or setting specific elements
Hash Table
- A hash table is very similar to an alist
- A hash table literal is preceded with a #Sto distinguish it from a list and an array
- Similar to arrays hash tables have a constant look up time
- Hash tables are less efficient than alists for really small tables
- Very large hash tables can be paged out to virtual memory which could have poor performance
- Lisp will sometimes need to reallocate the memory for the hash table when inserting a key
- This will cause an occasional slow key insertion
- Both arrays and hash tables are not considered very lispy
- They are best avoided until performance concerns arise
Structures
defstruct
- The defstructmacro is useful for building structured data out of lists
- The defstructmacro will also create functions for building and accessing the data from the struct
(defstruct rectangle x y width height) (defparameter *my-rect* (make-rectangle :x 10 :y 10 :width 50 :height 25)) (rectangle-height *my-rect*) (setf (rectangle-x *my-rect*) 85) *my-rect*
- The above example shows creating a structure with defstructand building one with themake-<STRUCT_NAME>function
- It also shows using a generated accessor function <STRUCT_NAME>-<PROPERPTY_NAME>
- The above example produces the following struct literal, #S(RECTANGLE :X 85 :Y 10 :WIDTH 50 :HEIGHT 25)
Default Values
- It is possible to provide default values to a struct by wrapping the slot with parenthesis and providing a value
(defstruct rectangle (x 10) (y 10) (width 100) (height 100)) (make-rectangle)
- The above example shows defining a struct with default values
- When initializing the struct the slots can be omitted and they will use the default values
- The above example returns the following struct literal, #S(RECTANGLE :X 10 :Y 10 :WIDTH 100 :HEIGHT 100)
Including Another Structure
- It is possible to declare a struct that includes the slots from another struct
- The syntax for doing this is to wrap the structure name in parenthesis then add a (:include some-other-struct)inside
(defstruct point (x 5) (y 5)) (defstruct (circle (:include point)) (radius 4)) (make-circle :x 25 :y 30 :radius 10)
- The above example shows creating a circlestructure that inherits all the slot from thepointstruct
- This example returns this struct literal, #S(CIRCLE :X 5 :Y 5 :RADIUS 4)
Streams
- There are two types of streams inputandoutput
- The predicates input-stream-pandoutput-stream-pwill indicate which direction a stream is
- The standard printandreadfunctions work with streams
- Streams support most of the functions that lists do with the exception of setf
- A string stream is useful for testing code that operates on streams
(with-output-to-string (*standard-output*) (princ "Hello world!"))
- The above example shows a with form that will collect anything that would normally be sent to standard output and instead return it as a string
Quick Lisp
- Quicklisp is a library manager (package manager) for Common Lisp
Installation
- Download quicklisp.lispform their website,curl -O https://beta.quicklisp.org/quicklisp.lisp
- Download the PGP signature from their website, curl -O https://beta.quicklisp.org/quicklisp.lisp.asc
- Download the release signing public key, curl -O https://beta.quicklisp.org/release-key.txt
- Import the release signing key, gpg --import release-key.txt
- Verify the quicklisp.lispfile,gpg --verify quicklisp.lisp.asc quicklisp.lisp
- Start up a Common Lisp environment
- Inside the REPL run, (load "quicklisp.lisp")(Ensure the REPL was started in the same folder Quicklisp was downloaded to)
- Run (quicklisp-quickstart:install)to install Quicklisp
- This will create a quicklispfolder in your home directory
- Normally when starting a new lisp session you would run (load "~/quicklisp/setup.lisp)to load Quicklisp into the running session
- Use (ql:add-to-init-file)to have this happen automatically
- Install the slime helper,(ql:quickload "quicklisp-slime-helper")- Be sure to follow the instructions
 
Installing Libraries
- This is an example of installing a package, (ql:quickload "vecto")
- This is an example of uninstalling a package, (ql:uninstall "vecto")
- To search for a library to install use apropos, (ql:system-apropos "sdl2")
Upgrading Quicklisp
- Use (ql:update-dist "quicklisp")to update Quicklisp
- Run (ql:update-client)to update the client
Examples
Split String into List of Words
(defun split-by-one-space (string) "Returns a list of substrings of string divided by ONE space each. Note: Two consecutive spaces will be seen as if there were an empty string between them." (loop for i = 0 then (1+ j) as j = (position #\Space string :start i) collect (subseq string i j) while j))